import cv2  # 导入OpenCV库，用于图像处理
import numpy as np  # 导入NumPy库，用于数组操作
import matplotlib.pyplot as plt  # 导入Matplotlib库，用于图像显示
from PIL import Image, ImageOps
import matplotlib.pyplot as plt

# 设置字体为SimHei
plt.rcParams['font.sans-serif'] = ['SimHei']

# 打开图片
img = Image.open('hanzi1.jpg')

# 根据EXIF数据调整图像方向
img = ImageOps.exif_transpose(img)

# 获取图片尺寸
width, height = img.size

# 计算裁剪后的图片尺寸
new_width = 2860
new_height = 3580

# 计算裁剪区域
left = (width - new_width) / 2
top = (height - new_height) / 2 +250  # 上方多裁剪100像素
right = (width + new_width) / 2 +50
bottom = (height + new_height) / 2 +50

# 裁剪图片
img_cropped = img.crop((left, top, right, bottom))


# 转化为灰度图
img_gray = img_cropped.convert('L')

# 全局阈值处理，得到二值化图
# 原本的黑色字体变成白色，其他部分变成黑色
threshold = 128
img_binary = img_gray.point(lambda p: p < threshold and 255 or 0)

# 将PIL图像转换为OpenCV格式
img_cv = np.array(img_binary)

# 创建结构元素，这里是3x3的正方形
kernel = np.ones((3, 3), np.uint8)

# 进行腐蚀操作，迭代4次增强腐蚀效果
eroded_img = cv2.erode(img_cv, kernel, iterations=4)

# 进行膨胀操作，迭代8次增强膨胀效果
dilated_img = cv2.dilate(eroded_img, kernel,iterations=8)

# 进行中值滤波，卷积核大小设为27
filtered_img = cv2.medianBlur(dilated_img, 27)

# 进行闭运算，迭代50次增强效果
closed_img = cv2.morphologyEx(filtered_img, cv2.MORPH_CLOSE, kernel,iterations=50)

# 定义自动确定Canny边缘检测的阈值函数
def auto_canny(image, sigma=0.33):
    # 计算图像的中值
    v = np.median(image)

    # 使用中值自动确定上下阈值
    lower = int(max(0, (1.0 - sigma) * v))
    upper = int(min(255, (1.0 + sigma) * v))

    # 使用Canny边缘检测器
    edged = cv2.Canny(image, lower, upper)

    # 返回边缘检测后的图像
    return edged

img_blur = cv2.GaussianBlur(closed_img,(3,3), 0) #高斯滤波
edges_with_blur = auto_canny(img_blur)

# 找到Canny边缘检测结果中的轮廓
contours, _ = cv2.findContours(edges_with_blur, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

# 在原图上画出轮廓
img_contours = cv2.cvtColor(np.array(img_cropped), cv2.COLOR_RGB2BGR)
for contour in contours:
    x, y, w, h = cv2.boundingRect(contour)
    
    # 扩大矩形框的范围
    padding = 30  # 可以根据需要调整
    x = max(0, x - padding)
    w = w + 2 * padding
    h = h + padding

    cv2.rectangle(img_contours, (x, y), (x+w, y+h), (0, 255, 0), 2)

# 保存每个轮廓对应的图像
import os
if not os.path.exists('chars'):
    os.makedirs('chars')

for i, contour in enumerate(contours):
    # 获取轮廓的边界框
    x, y, w, h = cv2.boundingRect(contour)
    
    # 扩大矩形框的范围
    padding = 30  # 可以根据需要调整
    x = max(0, x - padding)
    w = w + 2 * padding
    h = h + padding
    
    # 从原图中切出轮廓对应的部分
    char_img = img_cropped.crop((x, y, x+w, y+h))
    
    # 保存图像
    char_img.save(f'chars/char_{i}.jpg')


# 设置绘图区域，分为1行6列
fg, ax = plt.subplots(1, 7, figsize=(35, 5))

# 显示灰度图
ax[0].imshow(img_gray, cmap='gray')
ax[0].set_title('灰度图', size=15)
ax[0].axis('off')

# 显示二值图
ax[1].imshow(img_binary, cmap='gray')
ax[1].set_title('二值图', size=15)
ax[1].axis('off')

# 显示腐蚀后的图像
ax[2].imshow(eroded_img, cmap='gray')
ax[2].set_title('腐蚀图', size=15)
ax[2].axis('off')

# 显示滤波后的图像
ax[3].imshow(filtered_img, cmap='gray')
ax[3].set_title('膨胀图', size=15)
ax[3].axis('off')

# 显示闭运算后的图像
ax[4].imshow(closed_img, cmap='gray')
ax[4].set_title('闭运算图', size=15)
ax[4].axis('off')

# 显示高斯滤波后的Canny边缘检测结果
ax[5].imshow(edges_with_blur, cmap='gray')
ax[5].set_title('Canny边缘检测', size=15)
ax[5].axis('off')

# 显示带有轮廓的原图
ax[6].imshow(img_contours)
ax[6].set_title('带有轮廓的原图', size=15)
ax[6].axis('off')

# 显示图形
plt.show()

